package org.fisco.bcos.asset.client;

import org.fisco.bcos.asset.contract.Asset;
import org.fisco.bcos.asset.contract.SupplierFinancin;
import org.fisco.bcos.sdk.BcosSDK;
import org.fisco.bcos.sdk.abi.datatypes.generated.tuples.generated.*;
import org.fisco.bcos.sdk.client.Client;
import org.fisco.bcos.sdk.crypto.keypair.CryptoKeyPair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.util.*;

public class SFClient {
    public ArrayList<Client> clients=new ArrayList<>();
    public ArrayList<CryptoKeyPair> cpks=new ArrayList<>();
    static Logger logger = LoggerFactory.getLogger(SFClient.class);

    private BcosSDK bcosSDK;
    private Client client,client1,client2;
    private CryptoKeyPair cryptoKeyPair,cryptoKeyPair1,cryptoKeyPair2;
    public int current;
    public void initialize() throws Exception {
        // 函数initialize中进行初始化
        // 初始化BcosSDK
        @SuppressWarnings("resource")
        ApplicationContext context =
                new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        bcosSDK = context.getBean(BcosSDK.class);
        // 初始化可向群组1发交易的Client
        client = bcosSDK.getClient(1);
        // 随机生成发送交易的公私钥对
        cryptoKeyPair = client.getCryptoSuite().createKeyPair();
        client.getCryptoSuite().setCryptoKeyPair(cryptoKeyPair);
        client1=bcosSDK.getClient(1);
        cryptoKeyPair1=client1.getCryptoSuite().createKeyPair();
        client1.getCryptoSuite().setCryptoKeyPair(cryptoKeyPair1);
        client2=bcosSDK.getClient(1);
        cryptoKeyPair2=client2.getCryptoSuite().createKeyPair();
        client2.getCryptoSuite().setCryptoKeyPair(cryptoKeyPair2);

        clients.add(client);
        clients.add(client1);
        clients.add(client2);
        cpks.add(cryptoKeyPair);
        cpks.add(cryptoKeyPair1);
        cpks.add(cryptoKeyPair2);

        logger.debug("create client for group1, account address is " + cryptoKeyPair.getAddress());
        System.out.println("create client for group1, client account address is " + cryptoKeyPair.getAddress());
        System.out.println("create client for group1, client1 account address is " + cryptoKeyPair1.getAddress());
        System.out.println("create client for group1, client2 account address is " + cryptoKeyPair2.getAddress());
    }
    public void recordAssetAddr(String address) throws FileNotFoundException, IOException {
        Properties prop = new Properties();
        prop.setProperty("address", address);
        final Resource contractResource = new ClassPathResource("contract.properties");
        FileOutputStream fileOutputStream = new FileOutputStream(contractResource.getFile());
        prop.store(fileOutputStream, "contract address");
    }
    public String loadAssetAddr() throws Exception {
        // load Asset contact address from contract.properties
        Properties prop = new Properties();
        final Resource contractResource = new ClassPathResource("contract.properties");
        prop.load(contractResource.getInputStream());

        String contractAddress = prop.getProperty("address");
        if (contractAddress == null || contractAddress.trim().equals("")) {
            throw new Exception(" load Asset contract address failed, please deploy it first. ");
        }
        logger.info(" load Asset address from contract.properties, address is {}", contractAddress);
        return contractAddress;
    }
    public void registerbank(Client c,CryptoKeyPair ckp,String assetAccount,String uscc) {
        try {
            String contractAddress = loadAssetAddr();
            SupplierFinancin asset = SupplierFinancin.load(contractAddress, c, ckp);
            Tuple2<Boolean, String> ret=asset.getRegisterBankOutput(asset.registerBank(assetAccount,uscc));
            if(ret.getValue1()==false){
                System.out.println("failed : "+ret.getValue2());
            }else{
                System.out.println(ret.getValue2());
            }
        } catch (Exception e) {
            System.out.printf(" load asset account failed, error message is %s\n", e.getMessage());
        }
    }
    public void registercompany(Client c,CryptoKeyPair ckp,String assetAccount,String uscc) {
        try {
            String contractAddress = loadAssetAddr();
            SupplierFinancin asset = SupplierFinancin.load(contractAddress, c, ckp);
            Tuple2<Boolean, String> ret=asset.getRegisterCompanyOutput(asset.registerCompany(assetAccount,uscc));
            if(ret.getValue1()==false){
                System.out.println("failed : "+ret.getValue2());
            }else{
                System.out.println(ret.getValue2());
            }
        } catch (Exception e) {
            System.out.printf(" load asset account failed, error message is %s\n", e.getMessage());
        }
    }
    public void deployAssetAndRecordAddr() {
        try {
            SupplierFinancin asset = SupplierFinancin.deploy(clients.get(current), cpks.get(current));
            System.out.println(
                    " deploy Asset success, contract address is " + asset.getContractAddress());

            recordAssetAddr(asset.getContractAddress());
        } catch (Exception e) {
            System.out.println(" deploy Asset contract failed, error message is  " + e.getMessage());
        }
    }
    public String getAdmin(){
        try{
            String contractAddress = loadAssetAddr();
            SupplierFinancin asset = SupplierFinancin.load(contractAddress, client1, cryptoKeyPair1);
            Tuple1<String> str=asset.getGetAdminOutput(asset.getAdmin());
            return str.getValue1();
        }catch (Exception e){

        }
        return "failed to getadmin!";
    }
    public Tuple6<Boolean, String, String, BigInteger, BigInteger, BigInteger> select_company(Client c, CryptoKeyPair ckp){
        try{
            String contractAddress = loadAssetAddr();
            SupplierFinancin asset = SupplierFinancin.load(contractAddress,c,ckp);
            return asset.getSelectCompanyOutput(asset.selectCompany(ckp.getAddress()));
        }catch (Exception e){

        }
        return null;
    }
    public void print_company(int index){
        try {
            Tuple6<Boolean, String, String, BigInteger, BigInteger, BigInteger> temp=select_company(clients.get(index),cpks.get(index));
            if(temp.getValue1()==false){
                NullPointerException e=new NullPointerException(temp.getValue2());
                throw e;
            }
            System.out.println("    "+temp.getValue3()+" "+temp.getValue4()+" "+temp.getValue5()+" "+temp.getValue6());
        }catch (NullPointerException e){
            System.out.println("failed : "+e.getMessage());
        }

    }

    public void select_account(){

        for(int i=0;i<clients.size();i++){
            try{
                System.out.println(i+" : "+cpks.get(i).getAddress());
                Tuple6<Boolean, String, String, BigInteger, BigInteger, BigInteger> temp=select_company(clients.get(i),cpks.get(i));
                if(temp.getValue1()==false){
                    NullPointerException e=new NullPointerException(temp.getValue2());
                    throw e;
                }
                System.out.println("    "+temp.getValue3()+" "+temp.getValue4()+" "+temp.getValue5()+" "+temp.getValue6());
            }catch (NullPointerException e){
                System.out.println("    "+"failed : "+e.getMessage());
            }
        }
        System.out.println("please enter the account_num : ");
        Scanner scan = new Scanner(System.in);
        int num=scan.nextInt();
        current=num;

    }

    public List<BigInteger> get_receipt_all_id(String tableName, String key){
        try{
            String contractAddress = loadAssetAddr();
            SupplierFinancin asset = SupplierFinancin.load(contractAddress,clients.get(current),cpks.get(current));
            Tuple1<List<BigInteger>> ret=asset.getGetReceiptAllIdOutput(asset.getReceiptAllId(tableName,key));
            return ret.getValue1();
        }catch (Exception e){
            System.out.printf(" load asset account failed, error message is %s\n", e.getMessage());
        }
        return null;
    }
    public void print_receipt(String tableName, String key, BigInteger receiptId){
        try{
            String contractAddress = loadAssetAddr();
            SupplierFinancin asset = SupplierFinancin.load(contractAddress,clients.get(current),cpks.get(current));
            Tuple7<Boolean, String, String, String, BigInteger, BigInteger, BigInteger> ret=asset.getSelectReceiptOutput(asset.selectReceipt(tableName,key,receiptId));
            System.out.println("    "+ret.getValue3()+" "+ret.getValue4());
            System.out.println("    "+ret.getValue5()+" "+ret.getValue6()+" "+ret.getValue7());
            if(ret.getValue1()==false){
                System.out.println("failed : "+ret.getValue2());
            }else{
                System.out.println(ret.getValue2());
            }
        }catch (Exception e){
            System.out.printf(" load asset account failed, error message is %s\n", e.getMessage());
        }
    }
    public void transfer_receipt(String debtee, BigInteger amount, BigInteger deadline){
        try{
            String contractAddress = loadAssetAddr();
            SupplierFinancin asset = SupplierFinancin.load(contractAddress,clients.get(current),cpks.get(current));
            Tuple2<Boolean, String> ret=asset.getTransferReceiptOutput(asset.transferReceipt(debtee,amount,deadline));
            if(ret.getValue1()==false){
                System.out.println("failed : "+ret.getValue2());
            }else{
                System.out.println(ret.getValue2());
            }
        }catch (Exception e){
            System.out.printf(" load asset account failed, error message is %s\n", e.getMessage());
        }
    }
    public void return_receipt(BigInteger receiptId, BigInteger amount){
        try{
            String contractAddress = loadAssetAddr();
            SupplierFinancin asset = SupplierFinancin.load(contractAddress,clients.get(current),cpks.get(current));
            Tuple2<Boolean, String> ret=asset.getReturnReceiptOutput(asset.returnReceipt(receiptId,amount));
            if(ret.getValue1()==false){
                System.out.println("failed : "+ret.getValue2());
            }else{
                System.out.println(ret.getValue2());
            }
        }catch (Exception e){
            System.out.printf(" load asset account failed, error message is %s\n", e.getMessage());
        }
    }


    public static void mystart() throws Exception {

        SFClient sfclient = new SFClient();
        sfclient.initialize();
        Scanner scan = new Scanner(System.in);
        while(true){
            try{
                System.out.println("A.select account"+"     "+"B.register bank"+"     "+"C.register company");
                System.out.println("D.get admin_bank address"+"     "+"E.print company"+"     "+"F.get receipt all id");
                System.out.println("G.print receipt"+"     "+"H.transfer receipt"+"     "+"I.return receipt");
                System.out.println("J.deploy contract");
                System.out.print("please enter the option:");
                while(!scan.hasNextLine())
                    continue;
                char ch=scan.nextLine().charAt(0);
                String[] temp;
                switch (ch){
                    case 'A':
                        sfclient.select_account();
                        break;
                    case 'B':
                        System.out.print("please enter the account_number and uscc: ");
                        temp=scan.nextLine().split(" ");
                        sfclient.registerbank(sfclient.clients.get(sfclient.current),sfclient.cpks.get(sfclient.current),sfclient.cpks.get(Integer.parseInt(temp[0])).getAddress(),temp[1]);
                        break;
                    case 'C':
                        System.out.print("please enter the account_number and uscc: ");
                        temp=scan.nextLine().split(" ");
                        sfclient.registercompany(sfclient.clients.get(sfclient.current),sfclient.cpks.get(sfclient.current),sfclient.cpks.get(Integer.parseInt(temp[0])).getAddress(),temp[1]);
                        break;
                    case 'D':
                        String str=sfclient.getAdmin();
                        System.out.println(str);
                        break;
                    case 'E':
                        System.out.println("please enter the account_num : ");
                        temp=scan.nextLine().split(" ");
                        sfclient.print_company(Integer.parseInt(temp[0]));
                        break;
                    case 'F':
                        System.out.println("please enter tablename and account_num : ");
                        temp=scan.nextLine().split(" ");
                        List<BigInteger> lb=sfclient.get_receipt_all_id(temp[0],sfclient.cpks.get(Integer.parseInt(temp[1])).getAddress());
                        System.out.println(lb.toString());
                        break;
                    case 'G':
                        System.out.println("please enter tablename, account_num and receipt_id : ");
                        temp=scan.nextLine().split(" ");
                        sfclient.print_receipt(temp[0],sfclient.cpks.get(Integer.parseInt(temp[1])).getAddress(),new BigInteger(temp[2]));
                        break;
                    case 'H':
                        System.out.println("please enter account_num, amount and deadline : ");
                        temp=scan.nextLine().split(" ");
                        sfclient.transfer_receipt(sfclient.cpks.get(Integer.parseInt(temp[0])).getAddress(),new BigInteger(temp[1]),new BigInteger(temp[2]));
                        break;
                    case 'I':
                        System.out.println("please enter receipt_id and amount : ");
                        temp=scan.nextLine().split(" ");
                        sfclient.return_receipt(new BigInteger(temp[0]),new BigInteger(temp[1]));
                        break;
                    case 'J':
                        sfclient.deployAssetAndRecordAddr();
                        break;
                    default:
                }
            }catch (Exception e){
                //System.out.println(e.getMessage());
                e.printStackTrace();
            }
        }
//        sfclient.deployAssetAndRecordAddr();
//        sfclient.registerbank(sfclient.cryptoKeyPair1.getAddress(),"000002");
//
//        //new a client by hexprivatekey
//        Client c=sfclient.bcosSDK.getClient(1);;
//        CryptoKeyPair t=c.getCryptoSuite().createKeyPair(sfclient.cryptoKeyPair1.getHexPrivateKey());
//        c.getCryptoSuite().setCryptoKeyPair(t);
//        System.out.println(sfclient.cryptoKeyPair1.getHexPrivateKey());
//
//        sfclient.registercompany(c,t,sfclient.cryptoKeyPair2.getAddress(),"000003");
//        String str=sfclient.getAdmin();
//        System.out.println(str);
//
//
//        Tuple4<String, BigInteger, BigInteger, BigInteger> temp=sfclient.getOneCompanyAll(c,t);
//        System.out.println(temp.getValue1()+" "+temp.getValue2()+" "+temp.getValue3()+" "+temp.getValue4());
//        temp=sfclient.getOneCompanyAll(sfclient.client2,sfclient.cryptoKeyPair2);
//        System.out.println(temp.getValue1()+" "+temp.getValue2()+" "+temp.getValue3()+" "+temp.getValue4());
//        switch (args[0]) {
//            case "deploy":
//                client.deployAssetAndRecordAddr();
//                break;
//            case "registerbank":
//                client.registerbank();
////            case "query":
////                if (args.length < 2) {
////                    Usage();
////                }
////                client.queryAssetAmount(args[1]);
////                break;
////            case "register":
////                if (args.length < 3) {
////                    Usage();
////                }
////                client.registerAssetAccount(args[1], new BigInteger(args[2]));
////                break;
////            case "transfer":
////                if (args.length < 4) {
////                    Usage();
////                }
////                client.transferAsset(args[1], args[2], new BigInteger(args[3]));
////                break;
////            default:
////            {
////                Usage();
////            }
//        }

    }
}
